package br.pucrio.inf.lac.konoha.hyuga.others.dstream;

import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.LinkedBlockingQueue;

import br.pucrio.inf.lac.konoha.hyuga.events.LocationUpdateEvent;

public class GridCharacteristicVector {
    
    // MetaData
    private int x;
    private int y;
    private boolean receivedData;
    
    // Density
    private double density;
    private TYPE   type;
    private TYPE   prevType;
    private STATUS status;
    private STATUS prevStatus;
    
    // Nodes
    private LinkedBlockingQueue<Map<String, LocationUpdateEvent>> nodesPrevWindow;
    private Map<String, LocationUpdateEvent> currentWindowNodes;
    private Map<String, LocationUpdateEvent> allNodes;
    
    public enum TYPE  {DENSE, SPARSE, TRANSITIONAL};
    public enum STATUS {SPORADIC, NORMAL};
    

    public GridCharacteristicVector(int x, int y, int batchSize) {
        this.x = x;
        this.y = y;
        
        this.receivedData = false;
        this.type         = TYPE.SPARSE;
        this.prevType     = TYPE.SPARSE;
        this.status       = STATUS.NORMAL;
        this.prevStatus   = STATUS.NORMAL;
        
        this.allNodes           = new HashMap<String, LocationUpdateEvent>();
        this.currentWindowNodes = new HashMap<String, LocationUpdateEvent>();
        this.nodesPrevWindow    = new LinkedBlockingQueue<>(batchSize);
    }

    public void updateCharacteristicVector(LocationUpdateEvent lu) {
        boolean containPrevBatches = false;
        
        for (Map<String, LocationUpdateEvent> batchNodes : nodesPrevWindow) {
            if (batchNodes.containsKey(lu.getOriginalID())) {
                containPrevBatches = true;
                batchNodes.remove(lu.getOriginalID());
                break;
            }
        }
        
        if (!containPrevBatches && !currentWindowNodes.containsKey(lu.getOriginalID())) {
            density++;
        }
        
        currentWindowNodes.put(lu.getOriginalID(), lu);
        allNodes.put(lu.getOriginalID(), lu);
        receivedData = true;
    }
    
    public void updateAndResetBatch() {
        if (nodesPrevWindow.remainingCapacity() <= 0) {
            Map<String, LocationUpdateEvent> oldestBatch = nodesPrevWindow.poll();
            density = density - oldestBatch.size();
            
            for (String id : oldestBatch.keySet()) {
                allNodes.remove(id);                
            }
        }
        nodesPrevWindow.add(new HashMap<String, LocationUpdateEvent>(currentWindowNodes));
    }
    
    public void saveAndResetStatus() {
        receivedData = false;
        prevType     = type; 
        prevStatus   = status;
        currentWindowNodes.clear();
    }

   public void clear() {
        this.allNodes.clear();
        this.currentWindowNodes.clear();
        this.nodesPrevWindow.clear();

        this.density      = 0;
        this.receivedData = false;
        this.type         = TYPE.SPARSE;
        this.prevType     = TYPE.SPARSE;
        this.status       = STATUS.NORMAL;
        this.prevStatus   = STATUS.NORMAL;
    }
    
    public boolean hasChangedClass() {
        return type != prevType;
    }
    
    public boolean hasReceivedData() {
        return receivedData;
    }

    // Gets and Sets
    public Map<String, LocationUpdateEvent> getNodes() {
        return allNodes;
    }
    
    public int getSize() {
        return allNodes.size();
    }
    
    public double getDensity() {
        return density;
    }
    
    public TYPE getType() {
        return type;
    }
    
    public TYPE getPrevType() {
        return prevType;
    }
    
    public STATUS getStatus() {
        return status;
    }
    
    public STATUS getPrevStatus() {
        return prevStatus;
    }
    
    public void setType(TYPE type) {
        this.type = type;
    }
    
    public void setStatus(STATUS status) {
        this.status = status;
    }
    
    public int getX() {
        return x;
    }
    
    public int getY() {
        return y;
    }

    
}
